package com.acme.bankapp.server;

import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

import com.acme.bankapp.exception.ClientExistsException;
import com.acme.bankapp.exception.DeserializationException;
import com.acme.bankapp.service.bank.BankService;

public class BankServer implements Runnable {

    protected static final String MESSAGE_BYE_PATTERN = "^bye\\s*$";
    protected static final String MESSAGE_REPORT_PATTERN = "^report\\s*$";
    protected static final String MESSAGE_ADD_PATTERN = "^add accounttype=(c|s);"
	    + "balance=(\\d+|\\d+\\.\\d+);"
	    + "(overdraft=(\\d+|\\d+\\.\\d+);){0,1}" + "name=\\w+;"
	    + "gender=(m|f);$";
    protected static final int DEFAULT_SERVER_PORT = 14519;

    private static BankServer bankServer = new BankServer();
    private static boolean nowRunning = false;

    public static void main(final String args[]) {
	BankServer server = new BankServer();
	while (true) {
	    server.run();
	}
    }

    public static void startServer(final BankService bankService) {
	bankServer.setBankService(bankService);
	if (nowRunning == false) {
	    Thread thread = new Thread(bankServer);
	    thread.start();
	    nowRunning = true;
	}
    }

    protected BankService bankService;

    protected ServerSocket providerSocket;

    protected Socket connection = null;
    protected ObjectOutputStream out = null;

    protected ObjectInputStream in = null;

    @Override
    public void run() {
	try {
	    providerSocket = new ServerSocket(DEFAULT_SERVER_PORT);
	    while (true) {
		String message = null;
		System.out.println("Waiting for connection.");
		connection = providerSocket.accept();
		System.out.println("Client connected.");
		out = new ObjectOutputStream(connection.getOutputStream());
		out.flush();
		in = new ObjectInputStream(connection.getInputStream());
		try {
		    do {
			try {
			    message = (String) in.readObject();
			    System.out.println("client>" + message);
			    if (message.matches(MESSAGE_BYE_PATTERN)) {
				sendMessage("bye");
			    } else if (message.matches(MESSAGE_REPORT_PATTERN)) {
				String report = bankService.getBalanceReport();
				sendMessage(report);
			    } else if (message.matches(MESSAGE_ADD_PATTERN)) {
				try {
				    String feed = message.split(" ")[1];
				    bankService.addClientFromString(feed);
				    sendMessage("Added successfully.");
				} catch (ClientExistsException e) {
				    sendMessage("The client is already exists.");
				} catch (DeserializationException e) {
				    sendMessage("Bad syntax. Try again.");
				}
			    } else {
				sendMessage("Unknown command.");
			    }

			} catch (ClassNotFoundException e) {
			    System.err
				    .println("Data received in unknown format");
			}
		    } while (message == null || !message.equals(MESSAGE_BYE_PATTERN));
		} catch (EOFException e) {
		    System.out.println("Client was disconnected :(");
		} finally {
		    in.close();
		    out.close();
		    connection.close();
		}
	    }
	} catch (IOException e) {
	    System.err.println("Oops, we have a little error here..");
	    e.printStackTrace();
	    System.exit(1);
	}
    }

    private void setBankService(final BankService bankService) {
	this.bankService = bankService;
    }

    void sendMessage(final String msg) {
	try {
	    out.writeObject(msg);
	    out.flush();
	    System.out.println("server>" + msg);
	} catch (IOException e) {
	    e.printStackTrace();
	}
    }

}
